package co.flyver.flyverrc; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.graphics.BitmapFactory; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Bundle; import android.os.IBinder; import android.preference.PreferenceManager; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.widget.Button; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import com.google.gson.reflect.TypeToken; import com.zerokol.views.JoystickView; import java.lang.reflect.Type; import co.flyver.Client.Client; import co.flyver.Client.JoystickListener; import co.flyver.IPC.JSONUtils; import co.flyver.IPC.SharedIPCKeys; import co.flyver.IPC.Tuples; import static co.flyver.IPC.SharedIPCKeys.*; import static co.flyver.IPC.Tuples.Quadruple; import static co.flyver.IPC.Tuples.Triple; import static co.flyver.IPC.Tuples.Tuple; public class MainControl extends Activity implements SensorEventListener, Client.ConnectionHooks { /* CONSTANTS */ private static final String BUTTONS = "BUTTONS"; private static final String GENERAL = "GENERAL"; private static final float STEP = 1; private static final String JOYSTICK = "JOYSTICK"; private static final int RESULT_SETTINGS = 1; private static final String _DEFAULT = "0"; /* END OF CONSTANTS */ private static String sServerIP; SensorManager mSensorManager; private boolean mSendData = false; private boolean mEmergencyStarted = false; private boolean mServiceStartRequested = false; private static Context sMainControlContext; public static Toast lastToast = null; ServiceConnection serviceConnection; static Client client; JoystickListener joystickListener; Thread joystickController; Intent jsonIntent = new Intent("co.flyver.SENDJSON"); BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { byte[] data = intent.getByteArrayExtra("bitmap"); Log.d(GENERAL, "Received data"); ImageView imageView = (ImageView) findViewById(R.id.pic); imageView.setImageBitmap(BitmapFactory.decodeByteArray(data, 0, data.length)); } }; IntentFilter intentFilter = new IntentFilter("co.flyver.UPDATE"); //Generic containers and derived type objects private static Quadruple<String, Float, Float, Float> jsonQuadruple = new Quadruple<>(); private static Type jsonQuadrupleTypes = new TypeToken<Quadruple<String, Float, Float, Float>>() {}.getType(); private static Triple<String, String, Float> jsonTriple = new Triple<>(); private static Type jsonTripleTypes = new TypeToken<Triple<String, String, Float>>() {}.getType(); private static Tuple<String, String> jsonTuple = new Tuple<>(); private static Type jsonTupleTypes = new TypeToken<Tuple<String, String>>() {}.getType(); private SharedPreferences sharedPreferences; public static Context getMainCtrlContext() { return sMainControlContext; } public static void setServerIP() { SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getMainCtrlContext()); Log.d(GENERAL, "Server ip is: " + sharedPreferences.getString("serverIp", _DEFAULT )); MainControl.sServerIP = sharedPreferences.getString("serverIp", ""); Client.setServerIP(sServerIP); } @Override public void onSensorChanged(SensorEvent event) { } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } @Override public void onResume() { super.onResume(); registerToSensors(); registerReceiver(broadcastReceiver, intentFilter); } @Override public void onPause() { super.onPause(); mSensorManager.unregisterListener(this); unregisterReceiver(broadcastReceiver); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the strKey bar if it is present. getMenuInflater().inflate(R.menu.main_control, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle strKey bar item clicks here. The strKey bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); switch(id) { case R.id.action_settings: { Intent i = new Intent(this, SettingsActivity.class); startActivityForResult(i, RESULT_SETTINGS); } break; } return id == R.id.action_settings || super.onOptionsItemSelected(item); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case RESULT_SETTINGS: { applySettings(); } } } public void applySettings() { setServerIP(); float p; float i; float d; String preferenceValue; preferenceValue = sharedPreferences.getString("proportionalY", _DEFAULT); p = Float.parseFloat(preferenceValue.equals("") ? String.valueOf(0) : preferenceValue); preferenceValue = sharedPreferences.getString("integralY", _DEFAULT); i = Float.parseFloat(preferenceValue.equals("") ? String.valueOf(0) : preferenceValue); preferenceValue = sharedPreferences.getString("derivativeY", _DEFAULT); d = Float.parseFloat(preferenceValue.equals("") ? String.valueOf(0) : preferenceValue); Log.d(GENERAL, "Yaw pid coefficients: Proportional: " + p + " Integral: " + i + " Derivative: " + d); jsonQuadruple = new Quadruple<>(PIDYAW, p, i, d); jsonIntent.putExtra("json", JSONUtils.serialize(jsonQuadruple, jsonQuadrupleTypes)); sendBroadcast(jsonIntent); preferenceValue = sharedPreferences.getString("proportionalP", _DEFAULT); p = Float.parseFloat(preferenceValue.equals("") ? String.valueOf(0) : preferenceValue); preferenceValue = sharedPreferences.getString("integralP", _DEFAULT); i = Float.parseFloat(preferenceValue.equals("") ? String.valueOf(0) : preferenceValue); preferenceValue = sharedPreferences.getString("derivativeP", _DEFAULT); d = Float.parseFloat(preferenceValue.equals("") ? String.valueOf(0) : preferenceValue); Log.d(GENERAL, "Pitch pid coefficients: Proportional: " + p + " Integral: " + i + " Derivative: " + d); jsonQuadruple = new Quadruple<>(PIDPITCH, p, i, d); jsonIntent.putExtra("json", JSONUtils.serialize(jsonQuadruple, jsonQuadrupleTypes)); sendBroadcast(jsonIntent); preferenceValue = sharedPreferences.getString("proportionalP", _DEFAULT); p = Float.parseFloat(preferenceValue.equals("") ? String.valueOf(0) : preferenceValue); preferenceValue = sharedPreferences.getString("integralP", _DEFAULT); i = Float.parseFloat(preferenceValue.equals("") ? String.valueOf(0) : preferenceValue); preferenceValue = sharedPreferences.getString("derivativeP", _DEFAULT); d = Float.parseFloat(preferenceValue.equals("") ? String.valueOf(0) : preferenceValue); Log.d(GENERAL, "Roll pid coefficients: Proportional: " + p + " Integral: " + i + " Derivative: " + d); jsonQuadruple = new Quadruple<>(PIDROLL, p, i, d); jsonIntent.putExtra("json", JSONUtils.serialize(jsonQuadruple, jsonQuadrupleTypes)); sendBroadcast(jsonIntent); } /** * Registers to the orientation sensors of the phone */ public void registerToSensors() { mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); Sensor orientationSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION); mSensorManager.registerListener(new SensorEventListener() { @Override public void onSensorChanged(SensorEvent event) { //if the Start button is released, do not send data to the server if (mSendData) { jsonQuadruple = new Quadruple<>(COORDINATES, event.values[0], event.values[2], event.values[1]); jsonIntent.putExtra("json", JSONUtils.serialize(jsonQuadruple, jsonQuadrupleTypes)); sendBroadcast(jsonIntent); } } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } }, orientationSensor, SensorManager.SENSOR_DELAY_NORMAL); } /** * Attaches onClick/onTouch event listeners to the relevant buttons */ public void addListeners() { ImageButton imageButton; Button button; /* Start/Stop hold button */ imageButton = (ImageButton) findViewById(R.id.holdstart); final ImageButton finalImageButton2 = imageButton; imageButton.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch(event.getAction()) { case MotionEvent.ACTION_DOWN : { mSendData = true; finalImageButton2.setImageResource(R.drawable.start_btn_on); Log.i(BUTTONS, "Holdstart pressed"); } break; case MotionEvent.ACTION_UP : { mSendData = false; finalImageButton2.setImageResource(R.drawable.start_btn); jsonQuadruple = new Quadruple<>(COORDINATES, 0.0f, 0.0f, 0.0f); jsonIntent.putExtra("json", JSONUtils.serialize(jsonQuadruple, jsonQuadrupleTypes)); sendBroadcast(jsonIntent); Log.i(BUTTONS, "Holdstart released"); } break; default : { } } return false; } }); /* End of Start/Stop */ /* Connect Button */ imageButton = (ImageButton) findViewById(R.id.connectButton); imageButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(mServiceStartRequested) { Log.e(BUTTONS, "Service start already requested!"); return; } if(sServerIP == null || sServerIP.isEmpty()) { if(lastToast != null) { lastToast.cancel(); } lastToast = Toast.makeText(getApplicationContext(), "Enter IP address", Toast.LENGTH_SHORT); lastToast.show(); return; } mServiceStartRequested = true; Log.d(BUTTONS, "Server IP is: " + sServerIP); final Intent intent = new Intent(getApplicationContext(), Client.class); serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Client.LocalBinder localBinder = (Client.LocalBinder) service; client = localBinder.getInstance(); startService(intent); Log.e("cl", "Server has been bound"); } @Override public void onServiceDisconnected(ComponentName name) { Log.e("cl", "Server has been bound"); } }; bindService(intent, serviceConnection, BIND_AUTO_CREATE); } }); /* End of Connect button */ /* Emergency stop button */ imageButton= (ImageButton) findViewById(R.id.emergencyStop); final ImageButton finalImageButton = imageButton; imageButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(!mEmergencyStarted) { jsonTuple = new Tuple<>(EMERGENCY, "start"); jsonIntent.putExtra("json", JSONUtils.serialize(jsonTuple, jsonTupleTypes)); sendBroadcast(jsonIntent); if(lastToast != null) { lastToast.cancel(); } lastToast = Toast.makeText(getApplicationContext(), "Emergency state enabled", Toast.LENGTH_SHORT); finalImageButton.setImageResource(R.drawable.emergency_btn_on); lastToast.show(); mEmergencyStarted = true; } else { jsonTuple = new Tuple<>(EMERGENCY, "stop"); jsonIntent.putExtra("json", JSONUtils.serialize(jsonTuple, jsonTupleTypes)); sendBroadcast(jsonIntent); if(lastToast != null) { lastToast.cancel(); } lastToast = Toast.makeText(getApplicationContext(), "Emergency state disabled", Toast.LENGTH_SHORT); finalImageButton.setImageResource(R.drawable.emergency_btn_off); lastToast.show(); mEmergencyStarted = false; } } }); /* End of emergency stop button */ /* Take picture button */ button = (Button) findViewById(R.id.takePicture); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { jsonTriple = new Triple<>(PICTURE, PICREADY, 1.0f); jsonIntent.putExtra("json", JSONUtils.serialize(jsonTriple, jsonTripleTypes)); sendBroadcast(jsonIntent); } }); /* End of take picture button */ /* Joystick view */ JoystickView joystickView = (JoystickView) findViewById(R.id.joystickView); joystickView.setOnJoystickMoveListener(new JoystickView.OnJoystickMoveListener() { @Override public void onValueChanged(int angle, int power, int direction) { int steps = (int) Math.floor((double) power / 10); switch(direction) { case JoystickView.FRONT : { Log.d(JOYSTICK, "Throttle increased with " + (STEP * steps) + "steps"); Triple<String, String, Float> jsonTriple = new Triple<>(THROTTLE, INCREASE, STEP * steps); jsonIntent.putExtra("json", JSONUtils.serialize(jsonTriple, jsonTripleTypes)); sendBroadcast(jsonIntent); } break; case JoystickView.BOTTOM : { Log.d(JOYSTICK, "Throttle decreased with " + (STEP * steps) + "steps"); jsonTriple = new Triple<>(THROTTLE, DECREASE, STEP * steps); jsonIntent.putExtra("json", JSONUtils.serialize(jsonTriple, jsonTripleTypes)); sendBroadcast(jsonIntent); } break; case JoystickView.BOTTOM_LEFT : { Log.d(JOYSTICK, "Not implemented yet"); } break; case JoystickView.RIGHT_BOTTOM : { Log.d(JOYSTICK, "Not implemented yet"); } break; case JoystickView.FRONT_RIGHT : { Log.d(JOYSTICK, "Not implemented yet"); } break; case JoystickView.LEFT_FRONT: { Log.d(JOYSTICK, "Not implemented yet"); } break; case JoystickView.LEFT: { Log.d(JOYSTICK, "Yaw increased with " + (STEP * steps) + "steps"); jsonTriple = new Triple<>(YAW, INCREASE, STEP * steps); jsonIntent.putExtra("json", JSONUtils.serialize(jsonTriple, jsonTripleTypes)); sendBroadcast(jsonIntent); } break; case JoystickView.RIGHT: { Log.d(JOYSTICK, "Yaw decreased with " + (STEP * steps) + "steps"); jsonTriple = new Triple<>(YAW, DECREASE, STEP * steps); jsonIntent.putExtra("json", JSONUtils.serialize(jsonTriple, jsonTripleTypes)); sendBroadcast(jsonIntent); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } break; default: { Log.d(JOYSTICK, "Default!!"); } break; } } }, JoystickView.DEFAULT_LOOP_INTERVAL); /* End of joystick view */ } /** * Entry point * Registers event/broadcast listeners * Connects to a server * @param savedInstanceState */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MainControl.sMainControlContext = getApplicationContext(); setContentView(R.layout.activity_main_control); joystickListener = new JoystickListener(getApplicationContext()); joystickController = new Thread(joystickListener); joystickController.start(); addListeners(); sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); setServerIP(); Client.registerConnectionHooks(this); Client.registerCallback("battery", new Client.ClientCallback() { @Override public void run(String json) { TextView batteryStatus = (TextView) findViewById(R.id.batteryStatus); Type type = new TypeToken<Tuple<String, String>>() {}.getType(); Tuple<String, String> status = JSONUtils.deserialize(json, type); batteryStatus.setText("Battery: " + status.getValue() + "%"); } }); Client.registerCallback("pidyaw", new Client.ClientCallback() { @Override public void run(String json) { Quadruple<String, Float, Float, Float> pidyaw; Type type = new TypeToken<Quadruple<String, Float, Float, Float>>() {}.getType(); pidyaw = JSONUtils.deserialize(json, type); SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).edit(); editor.putString("proportionalY", String.valueOf(pidyaw.getValue1())); editor.putString("integralY", String.valueOf(pidyaw.getValue2())); editor.putString("derivativeY", String.valueOf(pidyaw.getValue3())); editor.apply(); Log.d("PID", json); } }); Client.registerCallback("pidpitch", new Client.ClientCallback() { @Override public void run(String json) { Log.d("PID", json); Quadruple<String, Float, Float, Float> pidpitch; Type type = new TypeToken<Quadruple<String, Float, Float, Float>>() {}.getType(); pidpitch = JSONUtils.deserialize(json, type); SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).edit(); editor.putString("proportionalP", String.valueOf(pidpitch.getValue1())); editor.putString("integralP", String.valueOf(pidpitch.getValue2())); editor.putString("derivativeP", String.valueOf(pidpitch.getValue3())); editor.apply(); } }); Client.registerCallback("pidroll", new Client.ClientCallback() { @Override public void run(String json) { Quadruple<String, Float, Float, Float> pidroll; Type type = new TypeToken<Quadruple<String, Float, Float, Float>>() {}.getType(); pidroll = JSONUtils.deserialize(json, type); SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).edit(); editor.putString("proportionalR", String.valueOf(pidroll.getValue1())); editor.putString("integralR", String.valueOf(pidroll.getValue2())); editor.putString("derivativeR", String.valueOf(pidroll.getValue3())); editor.apply(); Log.d("PID", json); } }); Client.setLastToast(lastToast); Client.setMainContext(sMainControlContext); intentFilter.addCategory(Intent.CATEGORY_DEFAULT); registerReceiver(broadcastReceiver , intentFilter); } @Override public boolean dispatchGenericMotionEvent(MotionEvent motionEvent) { float x = motionEvent.getX(); float y = motionEvent.getY(); if((Math.abs(x) < 0.1) && (Math.abs(y) < 0.1)) { Log.e("ASDFS", "Threshold not reached, do not change values"); joystickListener.setActive(false); return false; } joystickListener.setActive(true); joystickListener.setX(x); joystickListener.setY(y); return true; } @Override public void onConnect() { ImageButton imageButton; imageButton = (ImageButton) findViewById(R.id.connectButton); final ImageButton finalImageButton1 = imageButton; finalImageButton1.setImageResource(R.drawable.connect_btn_on); } @Override public void onDisconnect() { ImageButton imageButton; imageButton = (ImageButton) findViewById(R.id.connectButton); final ImageButton finalImageButton1 = imageButton; finalImageButton1.setImageResource(R.drawable.connect_btn_off); mServiceStartRequested = false; } }